package com.haha.Common;

import com.haha.Beans.Media;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.*;
import org.springframework.beans.factory.annotation.Value;

import javax.crypto.KeyGenerator;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class FfmpegUtil {
    @Value("${upload.path}")
    private static String uploadPath;
    public static byte[] genAesKey ()  {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(128);
            return keyGenerator.generateKey().getEncoded();
        } catch (NoSuchAlgorithmException e) {
            return null;
        }
    }

    // 传入文件名，并创建其指定的文件夹；
    public static boolean CreateDir(String fileName){
        File newDirectory = new File(uploadPath + fileName);

        if (newDirectory.mkdirs()) {
            System.out.println("文件夹及其所有父目录创建成功。");
            return true;
        } else {
            System.out.println("文件夹创建失败。");
            return false;
        }
    }

    // 一、创建ffmpeg指令
    // 1. 读取视频、获取并保存第一帧，并返回保存路径

    // 2. 获取时长
    public static void getVideoDuration(String videoPath){

    }
    // 2. 返回视频所有帧
    // 3. 扣绿
    // 4. 合并
    // 5. 拼接
    // 6. 音频调整
    // 二、执行ffmpeg指令


//    @Value("${ffmpeg-path}")
//    private static String ffmpegPath;
//    @Value("${video-info.ffprobe-path}")
//    private static String ffprobePath;
    private static String ffmpegPath = "E:/Coding/IDEA/资源/ffmpeg-win64-gpl-shared/bin/ffmpeg.exe";
    private static String ffprobePath = "E:/Coding/IDEA/资源/ffmpeg-win64-gpl-shared/bin/ffprobe.exe";

    public static void GetFfmpegProcess(Media media) {
        List<String> command = new ArrayList<String>();
        command.add(ffprobePath);
        command.add("-i");
        command.add(media.getMediaPath());
        String outputStream = null;
        try{
//            System.out.println(command);
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(command);
            // 将标准输入流和错误输入流合并，并通过标准输入流读取信息
            builder.redirectErrorStream(true);
            Process p = builder.start();
            p.waitFor();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static String getFirstFrame(String filePath, String fileName){
        Process p = null;
        // ffmpeg -i input_video.mp4 -vf "select=eq(n\,0)" -vframes:v 1 -q:v 2 output_image.png
        List<String> command = new ArrayList<String>();
        command.add(ffmpegPath);
        command.add("-i");
        command.add(filePath + fileName);
        command.add("-vf");
        command.add("\"select=eq(n\\,0)\"");
        command.add("-vframes:v");
        command.add("1");
        command.add("-q:v");
        command.add("2");
        String output = filePath + "FirstFrame_" + fileName.replaceAll(".mp4", ".png");
        command.add(output);
        try{
//            System.out.println(command);
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(command);
            // 将标准输入流和错误输入流合并，并通过标准输入流读取信息
//            builder.redirectErrorStream(true);
            p = builder.start();
//            p.waitFor();

            int exitCode = p.waitFor();
            if (exitCode != 0) {
                System.err.println("ffmpeg 命令执行失败，退出代码：" + exitCode);
            }
            return output;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (p != null) {
                p.destroy();
            }
        }
        return "错误信息：获取首帧失败！";
    }

    // 获取音视频的信息
    public static String[] runFfprobe(String filePath){
        Process process = null;
        List<String> commands = new ArrayList<String>();
        try {
            commands.add(ffprobePath);

            commands.add("-v");
            commands.add("quiet");
            // 格式信息
            commands.add("-show_format");
            // 流信息，如视频流、音频流
//            commands.add("-show_streams");
            // 输出格式为json
            commands.add("-print_format");
            commands.add("json");
            commands.add(filePath);

            // 创建 ProcessBuilder，执行 ffprobe 命令
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commands);
            process = builder.start();
            // 读取 ffprobe 的输出
//            StringBuilder output = new StringBuilder();
            String[] info = new String[3];
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    String[] parts = line.split(":");
                    if ("duration".equals(parts[0].replaceAll("\\s+|\"", ""))){
                        info[0] = parts[1];
                    } else if ("size".equals(parts[0].replaceAll("\\s+|\"", ""))){
                        info[1] = parts[1];
                    } else if ("format_name".equals(parts[0].replaceAll("\\s+|\"", ""))){
                        info[2] = parts[1];
                    }
//                    output.append(line);
                }
            }
            // 等待进程结束
            if (!process.waitFor(10, TimeUnit.SECONDS)) {
                System.err.println("ffprobe 命令执行失败，退出代码：" + false);
            }
            return info;
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (process != null && !process.isAlive()) {
                process.destroy();
                process.destroyForcibly();
            }
        }
        return null;
    }


    // 下面为通过JavaCv调用ffmpeg处理音视频
    // 获取视频第一帧的图像，格式为png
    public static String getFirstFrameByCv(String filePath, String fileName) throws FFmpegFrameGrabber.Exception {
        String videoFilePath = filePath + "/" + fileName;//视频文件路径
        String outputName = "FirstFrame_" + fileName.replaceAll(".mp4", ".png");
        String outputImagePath = filePath + "/" + outputName;// 输出图片的路径

        // 创建 FFmpegFrameGrabber 实例
        try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(videoFilePath)) {
            try {
                // 启动视频流
                grabber.start();

                // 循环直到获取到第一帧
                Frame frame;
                while ((frame = grabber.grabImage()) != null) {
                    // 检查是否已获取到帧
                    if (frame.image != null) {
                        // 转换为 BufferedImage
                        BufferedImage image;
                        try (Java2DFrameConverter converter = new Java2DFrameConverter()) {
                            image = converter.convert(frame);
                        }

                        // 保存第一帧为图片
                        File outputFile = new File(outputImagePath);
                        ImageIO.write(image, "png", outputFile);

                        System.out.println("第一帧已保存为: " + outputFile.getAbsolutePath());
                        break; // 找到第一帧后退出循环
                    }
                }
                return outputName;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 停止视频流
                grabber.stop();
            }
        } catch (FrameGrabber.Exception e) {
            throw new RuntimeException(e);
        }
        return  null;
    }
    // 获取视频的时长信息
    public static double getVideoDurationByCv(String filePath){
        double duration = -1;
        try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(filePath)) {
            try {
                grabber.start();
                AVFormatContext formatContext = grabber.getFormatContext();
                if (formatContext != null) {
                    // 从元数据中获取时长，并从微秒转换为秒
                    long metadata = formatContext.duration() / 1_000_000;

                    duration = (double) metadata;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                grabber.stop();
            }
        } catch (FrameGrabber.Exception e) {
            throw new RuntimeException(e);
        }
        return duration;
    }

    public static boolean checkVideoFile(String filePath){
        File directory = new File(filePath+"/");
        if (!directory.exists()){
            boolean isCreated = directory.mkdirs();

            if (isCreated){
                System.out.println("目录已成功创建: " + filePath);
                return true;
            }
            System.out.println("目录创建失败: " + filePath);
            return false;
        }
        System.out.println("目录已存在: " + filePath);
        return true;
    }

    public static void overlayVideoFiles(String[] files, VideoFilter filterStr) throws FFmpegFrameGrabber.Exception {
        FFmpegFrameGrabber[] grabbers = new FFmpegFrameGrabber[files.length];
        int[] frameRWH = new int[3];
        for (int i = 0; i < files.length; i++) {
            grabbers[i] = new FFmpegFrameGrabber(files[i]);
            grabbers[i].start();

            if ( i==0 ){
                frameRWH[0] = (int) grabbers[i].getFrameRate();
                frameRWH[1] = grabbers[i].getImageWidth();
                frameRWH[2] = grabbers[i].getImageHeight();
            }
        }

//        String filterStr = "[0:v]scale=iw/2:ih/2[a];[1:v][a]overlay=30:30[v]";
        try (FFmpegFrameFilter newFilter = new FFmpegFrameFilter(filterStr.toString(), frameRWH[1], frameRWH[2])) {
            newFilter.start();
            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(new File("streamOut.mp4"), frameRWH[1], frameRWH[2]);
            recorder.setFormat("mp4");
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            recorder.start();

            int idx = 0;
            Frame frameIn1, frameIn2, frameOut;
            while ((frameIn1 = grabbers[0].grabImage()) != null && (frameIn2 = grabbers[1].grabImage()) != null) {
                newFilter.push(frameIn1);
                newFilter.push(frameIn2);
                frameOut = newFilter.pullImage();
                if (frameOut != null && frameOut.image != null) {
                    recorder.record(frameOut);
                }
                if (idx++ > 100) {
                    break;
                }
            }
            recorder.close();
            newFilter.close();
            for (FFmpegFrameGrabber grabber : grabbers) {
                grabber.close();
            }
        } catch (FrameFilter.Exception | FrameRecorder.Exception | FrameGrabber.Exception e) {
            throw new RuntimeException(e);
        }
    }
}
